---
description: >
  Basic HTTPS response headers security in Ruby.
title: Secure Response Headers
---

_Check out [secure web development](/cheat-sheets/secure-web-development){:rel="noopener"}
and [secure response body](/cheat-sheets/secure-response-body){:rel="noopener"} for more notes._

This document assumes HTTPS has been enabled. Ruby samples focus on Roda. Check
its documentation for further details regarding where/how to use the code samples.

<details markdown="1" id="table-of-contents">
<summary>
Table of Contents
</summary>

* TOC
{:toc}
</details>

## Protect Browser

- Ensure browsers only access your site through HTTPS to prevent SslStrip attacks.

```ruby
response["Strict-Transport-Security"] = "max-age=31536000; includeSubdomains; preload"
```

- Prevent Clickjacking. Restrict where the response can be embedded to. Don't use
the associated meta tag, it has no effect.

```ruby
response["X-Frame-Options"] = "deny"
```

- Control which referrer info should be included with requests made. More granular
referrer policies can be set at view level.

```ruby
response["Referrer-Policy"] = "no-referrer"
```

- Deny clients, such as PDF readers, permission to handle data across domains.

```ruby
response["X-Permitted-Cross-Domain-Policies"] = none
```

## Cross-Site Scripting (XSS)

- Enable XSS protection and block rendering malicious pages.

```ruby
response["X-XSS-Protection"] = "1; mode=block"
```

- Stop browser from guessing content type in case users manage to store XSS attacks.

```ruby
response["X-Content-Type-Options"] = "nosniff"
```

### Content Security Policy

Restrict where the browser can load resources from to prevent the execution of
unknown scripts.

```ruby
# Web apps customize these according to their needs
cdns = %w[trusted-cdn.com other-cdn.com]
response["Content-Security-Policy"] = "default-src 'none'; script-src 'self' #{cdns}; connect-src 'self'; img-src 'self'; style-src 'self';"

# For pure JSON APIs
response["Content-Security-Policy"] = "default-src 'none'"
```

To help prevent web cache deception attacks add the `require-sri-for` directive.

```ruby
response["Content-Security-Policy"] = "require-sri-for script style; ..."
```

The [secure response body](https://search-and-deploy.gitlab.io/cheat-sheets/secure-response-body/#subresource-integrity){:rel="nofollow noreferrer noopener"}
cheat sheet has some details on how to add scripts, and styles using the
sub-resource integrity (SRI) check.

## Cookie Configuration

- Never store sensitive data in a cookie to help prevent replay attacks.
- Never hide cryptographic credentials in client software nor on client systems.
- Serve cookies only over HTTPS using the `Secure` flag.
- Limit cookie access level to `HttpOnly`.
- Use session cookies over persistent ones.
- Session secret:
  - Use a different secret token in each environment.
  - Inject secrets using env vars. Avoid hard coding them.
  - Set a `securerandom` number as a fallback.

```ruby
require "roda"

class Web < Roda
  use Rack::Session::Cookie, {
    key: "_app_session",
    path: "/",
    httponly: true,
    secure: true,
    expire_after: nil, # Session cookie
    secret: ENV.fetch("COOKIE_KEY") { SecureRandom.hex 64 }
  }
end
```

### Cross-Site Request Forgery (CSRF)

CSRF attacks affect end-points which change state on the server. Which
has more to do with a verb's safety than its idempotency.

According to OWASP, there are numerous ways to defend against CSRF. Yet,
a session cookie by itself is not enough to prevent this type of attack.
Although, when combined with token synchronization it makes a sound defence.

To avoid hidden pitfalls rely in a gem, toolkit, or framework that takes
care of CSRF protection. Make sure any non `GET` requests always require
a valid token, though.

```ruby
require "roda"

class WebSiteWithAjax < Roda

  # cookie configuration here

  plugin :type_routing,
    default_type: :html,
    exclude: :xml,
    types: {
      json: "application/json; charset=utf-8",
      html: "text/html; charset=utf-8"
    }

  plugin :csrf,
    raise: false
end
```


## Cross-Origin Resource Sharing (CORS)
CORS enables communication and resource sharing between trusted partners.

### Public Resources
These are dangerous since they allow connections from anywhere...

```ruby
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Origin"] = "null"
```

and neutralize credentials.

```ruby
response["Access-Control-Allow-Credentials"] = true # actually behaves like false
```

Wildcards aren't partials. They aren't safe to use:

```ruby
# Invalid
response["Access-Control-Allow-Origin"] = "https://*.example.com"

# Valid but UNSAFE. Accepts connections from http://evil.example.com
response["Access-Control-Allow-Origin"] = "*.example.com"
```

### Single Origin
By default CORS only works with a single origin. Sub-domains aren't included.

```ruby
response["Access-Control-Allow-Origin"] = "https://www.example.com/"
response["Access-Control-Allow-Credentials"] = true
response["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE"
```

### Multiple Origins

A common way of dealing with the single-origin limitation is to dynamically
consume the request header. Since that technique depends on input:

- Avoid trusting multiple origins.
- Rely on a well tested gem that:
  + Validates URL format.
  + Whitelist sub-domains.
  + Adds the `Vary` header with `Origin` value.

## Gems & Tools

Gems:
- [rack-secure_headers](https://github.com/frodsan/rack-secure_headers){:rel="nofollow noopener noreferrer"}.
- [rack-protection](https://github.com/sinatra/sinatra/tree/master/rack-protection){:rel="nofollow noopener noreferrer"}.
Help against typical web attacks.
- [rack-cors](https://github.com/cyu/rack-cors){:rel="nofollow noopener noreferrer"}.
- [rack-attack](https://github.com/kickstarter/rack-attack){:rel="nofollow noopener noreferrer"}. Request throttler.

Secure headers tools:
- [Observatory](https://observatory.mozilla.org/){:rel="nofollow noopener noreferrer"}. Header configuration checker.
- [Laboratory](https://addons.mozilla.org/en-US/firefox/addon/laboratory-by-mozilla/){:rel="nofollow noopener noreferrer"}.
CSP generator. Firefox web extension.
- [HSTS Preload Submission Form](https://hstspreload.org/){:rel="nofollow noopener noreferrer"}.

## Resources

### Guides & Cheat Sheets
- [Cross-Site Request Forgery Prevention Cheat Sheet](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet){:rel="nofollow noopener noreferrer"}
- [Testing for CSRF](https://www.owasp.org/index.php/Testing_for_CSRF_(OTG-SESS-005)){:rel="nofollow noopener noreferrer"}.
- [OWASP Secure Headers](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#tab=Headers){:rel="nofollow noopener noreferrer"}
- [Content Security Policy Reference](https://content-security-policy.com/){:rel="nofollow noopener noreferrer"}
- [OWASP Content Security Policy Cheat Sheet](https://www.owasp.org/index.php/Content_Security_Policy_Cheat_Sheet){:rel="nofollow noopener noreferrer"}

### Articles
- [Everything you need to know about HTTP security headers](https://blog.appcanary.com/2017/http-security-headers.html){:rel="nofollow noopener noreferrer"}
- [Mozilla Web Security Guidelines](https://wiki.mozilla.org/Security/Guidelines/Web_Security){:rel="nofollow noopener noreferrer"}
- [How HSTS' includeSubdomains works](https://www.ietf.org/mail-archive/web/websec/current/msg01365.html){:rel="nofollow noopener noreferrer"}
- [The Curious Case of TLS and the Missing Referrers](https://www.perpetual-beta.org/weblog/the-curious-case-of-tls-and-the-missing-referrers.html){:rel="nofollow noopener noreferrer"}
- [W3C Referrer Policy](https://w3c.github.io/webappsec-referrer-policy/){:rel="nofollow noopener noreferrer"}
- [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP){:rel="nofollow noopener noreferrer"}
- [CSP: require-sri-for](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-sri-for){:rel="nofollow noreferrer noopener"}
- [Block mixed content](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/block-all-mixed-content){:rel="nofollow noopener noreferrer"}
- [Web Application Security Working Group](https://github.com/w3c/webappsec){:rel="nofollow noopener noreferrer"}
- [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options){:rel="nofollow noopener noreferrer"}
- [X-XSS-Protection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection){:rel="nofollow noopener noreferrer"}
- [Stric Transport Security Cheat Sheet](https://www.owasp.org/index.php/HTTP_Strict_Transport_Security_Cheat_Sheet){:rel="nofollow noopener noreferrer"}
- [HSTS Preload List Submition](https://hstspreload.org/){:rel="nofollow noopener noreferrer"}
- [I'm Giving Up on HPKP](https://scotthelme.co.uk/im-giving-up-on-hpkp/){:rel="nofollow noopener noreferrer"}.
- [XSS Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet){:rel="nofollow noopener noreferrer"}
- [One Line of Code that Compromises Your Server](https://martinfowler.com/articles/session-secret.html){:rel="nofollow noopener noreferrer"}.
- [What are idempotent and/or safe methods?](https://restcookbook.com/HTTP%20Methods/idempotency/){:rel="nofollow noopener noreferrer"}
- [Reviewing Code for CSRF Issues](https://www.owasp.org/index.php/Reviewing_code_for_Cross-Site_Request_Forgery_issues){:rel="nofollow noopener noreferrer"}.
- [Cross-domain Ajax with Cross-Origin Resource Sharing](https://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/){:rel="nofollow noopener noreferrer"}.
- [HTTP Access Control (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS){:rel="nofollow noopener noreferrer"}.
